今天與工作室的Alvin談到廣告輪播(Banner)這件事情,於是就開啟了今天的主題。
網路上有許多教學,有使用ScrollView,也有使用CollectionView的,因為考慮到之後可能會有點擊事件,我後來選了CollectionView來實作。
--
一開始以為很簡單,沒想到其實不簡單,並且很多細節,這次我先暫時做一個簡易版本的,首先先宣告一個CollectionView以及其他變數:
//儲存螢幕寬度,省得每次都要打很多字
let width = UIScreen.main.bounds.width
//我先在素材庫存了七張圖片,這是儲存圖片的陣列
let imageArray: [UIImage] =
{
var arr = [UIImage]()
for i in 1...7
{
let image = UIImage(named: String(i))
arr.append(image!)
}
arr.append(UIImage(named: "1")!)
return arr
}()
//儲存當下顯示的圖片的索引
var imageIndex = 0
//宣告一個CollectionView
var collectionView: UICollectionView!
接著開始設定這個CollectionView:
func setupCollectionView()
{
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
// section與section之間的距離(如果只有一個section,可以想像成frame) 沒影響
layout.sectionInset = UIEdgeInsets.zero
// cell的寬、高
layout.itemSize = CGSize(width: width,
height: 200)
// cell與cell的間距
layout.minimumLineSpacing = CGFloat(integerLiteral: Int(0))
// cell與邊界的間距 沒影響
// layout.minimumInteritemSpacing = CGFloat(integerLiteral: 10)
// 滑動方向預設為垂直。注意若設為垂直,則cell的加入方式為由左至右,滿了才會換行;若是水平則由上往下,滿了才會換列
layout.scrollDirection = UICollectionView.ScrollDirection.horizontal
// 設定collectionView的大小
let rect = CGRect(x: 0, y: 20, width: width, height: width * (9 / 16))
self.collectionView = UICollectionView(frame: rect, collectionViewLayout: layout)
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.register(MyCell.self, forCellWithReuseIdentifier: "cell")
self.collectionView.isPagingEnabled = true
self.collectionView.backgroundColor = .clear
self.view.addSubview(collectionView)
}
並且在ViewDidLoad呼叫這個方法:
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
另外新增一個Class來管理Cell,我命名為MyCell:
class MyCell: UICollectionViewCell {
var imageView = UIImageView()
func setupImageView(){
imageView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 200)
imageView.backgroundColor = .lightGray
self.addSubview(imageView)
}
override func layoutSubviews() {
setupImageView()
}
}
接著遵從UICollectionViewDelegate、UICollectionViewDataSource:
class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource
並且實作因為遵從協議而必須實作的方法:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
cell.imageView.image = imageArray[indexPath.item]
return cell
}
到了這邊,基本上已經可以手動輪播了,但是我們要自動無限輪播,所以我們需要再做一個Timer,讓App可以自動輪播,將這行程式碼加在ViewDidLoad內:
Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(changeBanner), userInfo: nil, repeats: true)
計時器每隔2秒會呼叫changeBanner這個方法,這個方法是我們自己自訂的,程式碼如下:
@objc func changeBanner()
{
imageIndex += 1
let indexPath: IndexPath = IndexPath(item: imageIndex, section: 0)
if imageIndex < (imageArray.count - 1)
{
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}else if imageIndex == imageArray.count
{
print("Scroll to zero")
imageIndex = 0
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
changeBanner()
}
}
現在完成了自動輪播功能了,App看起來應該像這樣子,但是這只是很簡易的寫法,並且只有最初的框架,還需要再作修改:
我之前遇到一個 UX 的狀況,就是在與 banner 互動的時候要暫停輪轉,互動結束的時候等待時差後繼續輪轉。
後來的成果和程式碼都不錯~
你是說例如我現在點著item不放,背景的timer要暫停嗎?現在好像預設都會這樣了,因為我沒有額外寫這個功能,但是只要我點著item不放,timer就暫停,我放開之後timer才會繼續跑
不是,是離開點擊後,重新等時間間隔